# coding: utf-8

import json
import urllib
import os
from itertools import chain

from flask import (
    render_template, redirect, url_for, flash, request,
    jsonify,
)
from flask_login import login_required, current_user
from wtforms import StringField
from sqlalchemy import and_

from app import db
from app.models import get_timestamp
from app.products.constant import SpecUnit, DraftStatus
from app.products.forms import (
    ProductParamForm, ProductForm, ProductBaseForm, ProductSearchForm,
    ProductParamEditForm, ProductBatchForm, DraftApprovalForm, ParamSearchForm,
    DraftSearchForm)
from app.products.utils import all_params, recursion_params
from . import product_bp
from .models import TechnologyParams, ProductTemplate, ProductToParam, \
    ProductPrice, make_sure_json_list, ProductDraft, Brand, ProductDesc, \
    BrandModel


@product_bp.route('/params', methods=['GET', 'POST'])
def param_list():
    """
    技术参数列表
    :return:
    """
    search_form = ParamSearchForm(formdata=request.form or request.args)
    spec_query = TechnologyParams.generate_list_query_condition(request.args)

    for name, value in search_form._fields.items():
        if value.data:
            if isinstance(value, StringField):
                spec_query = spec_query.filter(
                    getattr(TechnologyParams, name).like(
                        '%{}%'.format(value.data)
                    )
                )
            else:
                spec_query = spec_query.filter_by(
                    **{name: value.data}
                )
    spec_query = spec_query.filter_by(deleted_at=0)

    pagination = spec_query.paginate(error_out=False)
    params = pagination.items

    return render_template(
        'param/param_list.html',
        search_form=search_form,
        params=params,
        pagination=pagination,
        url_kwargs=search_form.data,
    )


@product_bp.route('/add_param', methods=['GET', 'POST'])
def add_param():
    """
    新增产品技术参数
    :return:
    """
    form = ProductParamForm()

    if form.validate_on_submit():
        if not make_sure_json_list(form.options.data):
            flash('options字段值错误，更新失败')
            return redirect(url_for('product_bp.add_param'))
        param = TechnologyParams()
        form.populate_obj(param)
        db.session.add(param)
        db.session.flush()
        flash('新增技术参数成功')
        return redirect(url_for('product_bp.param_list'))
    return render_template(
        "/param/param_add.html",
        form=form,
    )


@product_bp.route('/params/<int:param_id>', methods=['GET', 'POST'])
def edit_param(param_id):
    """
    编辑产品规格
    :return:
    """

    spec = TechnologyParams.query.get_or_404(param_id)
    if request.method == 'POST':
        form = ProductParamEditForm()

        if not make_sure_json_list(form.options.data):
            flash('options字段值错误，更新失败')
            return redirect(url_for('product_bp.edit_param', param_id=param_id))

        if form.validate_on_submit():

            form.populate_obj(spec)
            form.parent_choices()
            # 计算项发生变化时，同步产品规格值的计算项
            ProductToParam.query.filter(
                ProductToParam.technology_param_id == spec.id
            ).update(
                dict(
                    calculate_item=spec.calculate_item,
                    name=spec.name,
                    is_display=spec.is_display,
                    unit=spec.unit,
                    is_required=spec.is_required,
                    value_type=spec.value_type,
                    parent_id=spec.parent_id,
                    tag_type=spec.tag_type,
                    placeholder=spec.placeholder,
                    options=spec.options,
                    display_rule=spec.display_rule,
                    weight=spec.weight
                )
            )
            db.session.add(spec)
            db.session.flush()
            flash('更新技术参数成功')
            return redirect(
                url_for(
                    'product_bp.param_list',
                    product_type_id=spec.product_type_id
                )
            )
        else:
            flash('编辑出错，请检查表单数据。')
            return redirect(url_for(
                'product_bp.edit_param', param_id=param_id)
            )
    else:
        form = ProductParamEditForm(obj=spec)
        form.parent_choices()
        # 编辑时产品类型不可修改
        form.product_type_id.choices = [
            (spec.product_type_id, spec.product_type_name)
        ]
        return render_template(
            "/param/param_info.html",
            form=form,
            param_id=param_id
        )


@product_bp.route('/ajax/params/parent_ids', methods=['POST'])
def ajax_param_parent_ids():
    """
    create by ld_wu 2017-12-29
    获取产品类下的可以作为父id的技术参数
    :return:
    """
    product_type_id = request.get_json().get('product_type_id')
    choices = [(0, '无')]
    if product_type_id:
        choices += TechnologyParams.params_parents_choices(product_type_id)
    return jsonify(
        choices
    )


@product_bp.route('/ajax/remove/param/<int:param_id>')
@login_required
def ajax_delete_param(param_id):
    """
    create_by ld_wu 2017-08-25
    移除产品规格
    """
    # FIXME by ld_wu 2017-08-25 暂时使用get请求
    ProductToParam.remove(technology_param_id=param_id)
    TechnologyParams.remove(id=param_id)
    form = ProductParamForm()
    search_form = ProductSearchForm(formdata=request.form or request.args)
    spec_query = TechnologyParams.generate_list_query_condition(request.args)

    for name, value in search_form._fields.items():
        if value.data:
            if isinstance(value, StringField):
                spec_query = spec_query.filter(
                    getattr(TechnologyParams, name).like(
                        '%{}%'.format(value.data)
                    )
                )
            else:
                spec_query = spec_query.filter_by(
                    **{name: value.data}
                )
    spec_query = spec_query.filter_by(deleted_at=0)
    pagination = spec_query.paginate(error_out=False)
    params = pagination.items
    flash('删除成功')

    return render_template(
        'param/param_list.html',
        search_form=search_form,
        form=form,
        params=params,
        pagination=pagination,
        url_kwargs=search_form.data,
    )


@product_bp.route('/add_product', methods=['GET', 'POST'])
def add_product():
    """
    新增产品
    :return:
    """
    form = ProductForm()
    form_pictures = json.loads(form.pictures.data)
    if form.validate_on_submit():
        spec_settings = json.loads(form.specs.data)
        form.pictures.data = form_pictures
        for each in spec_settings:
            value = each.get('values')
            if value:
                if not make_sure_json_list(value):
                    flash('规格参数错误')
                    return render_template(
                        "/product/product_add.html",
                        form=form,
                        form_prictures=form.pictures.data or list()
                    )
                each['values'] = json.loads(value)

        product = ProductTemplate()
        form.populate_obj(product)
        product.spec_settings = spec_settings
        brand = Brand.get_by_id(product.brand_id)
        if brand:
            product.brand = brand.brand_name
        db.session.add(product)
        db.session.flush()

        params = json.loads(form.params.data)
        if params:
            # 写入该产品规格值
            for param in params:
                ProductToParam.create(
                    product_template_id=product.id,
                    **param
                )
        flash('新增产品成功')
        return redirect(url_for('product_bp.product_list'))
    return render_template(
        "/product/product_add.html",
        form=form,
        form_pictures=form_pictures or list()
    )


@product_bp.route('/products', methods=['GET', 'POST'])
def product_list():
    """
    产品列表
    :return:
    """
    search_form = ProductSearchForm(formdata=request.form or request.args)
    product_query = ProductTemplate.generate_list_query_condition(request.args)
    for name, value in search_form._fields.items():
        if value.data:
            if isinstance(value, StringField):
                product_query = product_query.filter(
                    getattr(ProductTemplate, name).like(
                        '%{}%'.format(value.data)
                    )
                )
            else:
                product_query = product_query.filter_by(
                    **{name: value.data}
                )
    product_query = product_query.filter_by(deleted_at=0)
    pagination = product_query.paginate(error_out=False)
    products = pagination.items

    return render_template(
        '/product/product_list.html',
        search_form=search_form,
        products=products,
        pagination=pagination,
        url_kwargs=search_form.data,
    )


@product_bp.route('/product_info/<int:product_id>', methods=['GET'])
def product_info(product_id):
    """
    产品详情
    :return:
    """
    product = ProductTemplate.query.get_or_404(product_id)
    form = ProductBaseForm(obj=product)
    form.brand_id.choices = Brand.brand_choices(product.product_type_id)
    form.product_type_id.choices = [
        (product.product_type_id, product.product_type_name)
    ]
    form.brand_model_id.choices = [(0, '--请选择型号--')] + list(BrandModel.model_choices(
        product.brand_id
    ))
    params = ProductToParam.get_list_by_args(
        product_template_id=product_id)
    product_params_json = [dict(
        id=item.id,
        product_id=item.product_template_id,
        technology_param_id=item.technology_param_id,
        value=item.value,
        name=item.name,
        unit=item.unit,
        is_required=item.is_required,
        is_display=item.is_display,
        value_type=item.value_type,
        display_rule=item.display_rule,
        options=item.options,
        parent_id=item.parent_id,
        tag_type=item.tag_type,
        placeholder=item.placeholder
    ) for item in params]
    form.params.data = json.dumps(product_params_json)

    params = sorted(params, key=lambda x: not x.is_display)
    return render_template(
        "/product/product_info.html",
        form=form,
        product=product,
        product_params=params
    )


@product_bp.route('/edit_product/<int:product_id>', methods=['POST'])
def edit_product(product_id):
    """
    编辑产品
    :return:
    """
    product = ProductTemplate.query.get_or_404(product_id)
    form = ProductForm()

    if form.validate_on_submit():
        form_spec_settings = json.loads(form.specs.data)
        spec_mapping = {
            each.get('spec_templates_id') or each.get('id'): each
            for each in product.spec_settings or list()
        }
        for key, each in form_spec_settings.items():
            if each:
                if not make_sure_json_list(each):
                    flash('规格参数错误')
                    return redirect(url_for(
                        'product_bp.product_info', product_id=product_id)
                    )
                spec = spec_mapping.get(int(key))
                if spec:
                    spec['values'] = json.loads(each)

        if form.brand_id.data != product.brand_id:
            brand = Brand.get_by_id(form.brand_id.data)
            if brand:
                product.brand = brand.brand_name

        form.populate_obj(product)
        params_data = json.loads(form.params.data)
        product.spec_settings = list(spec_mapping.values())
        product.pictures = json.loads(form.pictures.data)
        db.session.add(product)
        db.session.flush()
        if params_data:
            params = ProductToParam.get_list_by_args(
                product_template_id=product_id
            )

            mapping = {each.id: each for each in params}
            for param in params_data:
                obj = mapping.get(param['technology_param_id'])
                if obj:
                    obj.value = param['value']
                    db.session.add(obj)

        # add by zhouhl 2017-09-22 更新产品后，同步更新产品的价格记录
        # tmp_record = ProductToPrice.get_by_args(
        #     product_id=product_id,
        #     user_id=0,
        # )
        # if tmp_record:
        #     tmp_record.price = product.price
        #     tmp_record.suggest_price = product.suggest_price

        flash('更新产品成功')
        return redirect(url_for('product_bp.product_list'))
    else:
        flash('编辑出错，请检查表单数据。')
        return redirect(url_for(
            'product_bp.product_info', product_id=product_id)
        )


@product_bp.route('/ajax/params')
def ajax_params():
    """
    通过产品类型获取产品规格（技术参数）
    :return:
    """
    product_type_id = request.args.get('product_type_id', type=int)
    params = list()
    if product_type_id:
        params = TechnologyParams.get_list_by_args(
            product_type_id=product_type_id
        )
        params = sorted(params, key=lambda x: not x.is_display)
        params = [dict(
            id=spec.id,
            name=spec.name,
            default=str(spec.default_value),
            unit_display=SpecUnit.mapping.get(spec.unit, ''),
            unit=spec.unit,
            calculate_item=spec.calculate_item,
            is_display=spec.is_display
        ) for spec in params]
    return jsonify(params)


@product_bp.route('/ajax/sync/params', methods=['POST'])
def ajax_sync_params():
    """
    同步（技术参数）
    :return:
    """
    try:
        product_id = request.get_json()['product_id']
        product_type_id = request.get_json()['product_type_id']
        product = ProductTemplate.get_by_id(product_id)
        technology_params = TechnologyParams.get_list_by_args(
            product_type_id=product_type_id
        )
        params = ProductToParam.get_list_by_args(
            product_template_id=product_id
        )

        param_map = {
            each.technology_param_id: each for each in params
        }

        for each in technology_params:
            # 如果产品模板没有该技术参数则新增技术参数
            if each.id not in param_map.keys():
                ProductToParam.create(
                    product_template_id=product.id,
                    technology_param_id=each.id,
                    name=each.name,
                    unit=each.unit,
                    calculate_item=each.calculate_item,
                    is_display=each.is_display,
                    is_required=each.is_required,
                    display_rule=each.display_rule,
                    value_type=each.value_type
                )
            # 已经存在技术参数的更新部分信息
            else:
                param = param_map[each.id]
                if param.name != each.name:
                    param.name = each.name
                if param.unit != each.unit:
                    param.unit = each.unit
                if param.calculate_item != each.calculate_item:
                    param.calculate_item = each.calculate_item
                param.weight = each.weight
                param.display_rule = each.display_rule
        # status=0为同步成功
        return jsonify(dict(status=0))
    except Exception as e:
        # status=1为同步失败
        return jsonify(dict(status=1))


@product_bp.route('/ajax/remove/product/<int:product_id>')
@login_required
def ajax_delete_product(product_id):
    """
    create_by ld_wu 2017-08-25
    移除产品
    """
    # FIXME by ld_wu 2017-08-25 暂时使用get请求

    ProductToParam.remove(product_template_id=product_id)
    ProductPrice.remove(product_template_id=product_id)
    ProductTemplate.remove(id=product_id)
    ProductDesc.remove(product_template_id=product_id)
    form = ProductForm()
    search_form = ProductSearchForm(formdata=request.form or request.args)
    product_query = ProductTemplate.generate_list_query_condition(request.args)
    for name, value in search_form._fields.items():
        if value.data:
            if isinstance(value, StringField):
                product_query = product_query.filter(
                    getattr(ProductTemplate, name).like(
                        '%{}%'.format(value.data)
                    )
                )
            else:
                product_query = product_query.filter_by(
                    **{name: value.data}
                )
    product_query = product_query.filter_by(deleted_at=0)
    pagination = product_query.paginate(error_out=False)
    products = pagination.items
    flash('删除成功')

    return render_template(
        '/product/product_list.html',
        form=form,
        search_form=search_form,
        products=products,
        pagination=pagination,
        url_kwargs=search_form.data,
    )


@product_bp.route('/batch/update/products', methods=['GET', 'POST'])
def batch_update_products():
    """
    批量更新产品
    :return:
    """
    search_form = ProductBatchForm(formdata=request.form or request.args)
    if search_form.validate_on_submit():
        query = ProductTemplate.query.filter(
            ProductTemplate.product_type_id == search_form.product_type_id.data
        ).join(
            ProductToParam,
            and_(
                ProductTemplate.id == ProductToParam.product_template_id,
                ProductToParam.technology_param_id == search_form.spec_id.data
            )
        ).with_entities(
            ProductTemplate.id.label('product_id'),
            ProductTemplate.name.label('name'),
            ProductTemplate.brand.label('brand'),
            ProductTemplate.source.label('source'),
            ProductToParam.value.label('value'),
            ProductToParam.id.label('product_to_spec_id'),
            ProductToParam.name.label('name')
        )
        search_form.spec_id.choices = TechnologyParams.specs_choices(
            search_form.product_type_id.data
        )

        products = query.all()
    else:
        products = list()

    return render_template(
        'product/batch_product_info.html',
        search_form=search_form,
        products=products,
        spec_map={each.product_to_spec_id: each.value for each in products}
    )


@product_bp.route('/ajax/product/params')
def product_params():
    """
    通过产品类获取产品规格(技术参数)
    :return:
    """
    product_type_id = request.args.get('product_type_id', type=int)
    if not product_type_id:
        params = list()
    else:
        params = TechnologyParams.specs_choices(product_type_id)

    return jsonify(params)


@product_bp.route('/ajax/batch/update/params', methods=['POST'])
def batch_update_params():
    """
    批量更新产品规格（技术参数）
    :return:
    """
    spec_map = request.get_json().get('spec_map') or dict()
    for spec_id, value in spec_map.items():
        spec = ProductToParam.get_by_id(spec_id)
        if spec:
            spec.value = value

    return jsonify(stauts=1)


@product_bp.route('/ajax/get_products')
def ajax_get_products():
    """
    add by zhouhl 2017-09-26
    AJAX接口，根据产品类型取产品列表
    """
    type_id = request.args.get('type_id', None)
    if type_id is None:
        return jsonify(choices=[])
    products = ProductTemplate.query.filter_by(product_type_id=type_id)
    choices = [{'id': i.id, 'name': i.name} for i in products]
    return jsonify(choices=choices)


@product_bp.route('/product_drafts', methods=['GET', 'POST'])
@login_required
def product_draft_list():
    """
    create by ld_wu 2018-01-02
    产品草稿列表
    :return:
    """
    search_form = DraftSearchForm(formdata=request.form or request.args)
    search_args = {
        key: value for key, value in search_form.data.items() if value
    }
    draft_query = ProductDraft.generate_list_query_condition(search_args)

    pagination = draft_query.paginate(error_out=False)
    drafts = pagination.items

    return render_template(
        'product/product_draft_list.html',
        search_form=search_form,
        drafts=drafts,
        pagination=pagination,
        url_kwargs=search_args
    )


@product_bp.route('/product_draft/<int:draft_id>')
@login_required
def product_draft_info(draft_id):
    """
    create by ld_wu 2018-01-02
    待审核产品详情
    """
    draft = ProductDraft.query.get_or_404(draft_id)
    form = DraftApprovalForm(obj=draft)

    param_info = list(chain(
        *all_params(
            ['otherParams', 'titleParams', 'requiredParams'],
            draft.product_info
        )
    ))
    param_info = recursion_params(param_info)
    param_mapping = dict()

    technology_param_ids = [
        param['technology_param_id']
        for param in param_info
        if param.get('technology_param_id')
    ]
    technology_params = TechnologyParams.get_list_by_ids(technology_param_ids)
    technology_param_mapping = {each.id: each for each in technology_params}

    current_pictures = list()
    if draft.product_template_id:
        param_ids = [param['id'] for param in param_info if 'id' in param]
        params = ProductToParam.get_list_by_ids(param_ids)
        param_mapping = {each.id: each for each in params}
        template = ProductTemplate.get_by_id(draft.product_template_id)
        current_pictures = template.pictures

    return render_template(
        'product/product_draft_info.html',
        draft=draft,
        param_mapping=param_mapping,
        technology_param_mapping=technology_param_mapping,
        current_pictures=current_pictures,
        form=form,
        param_info=param_info
    )


@product_bp.route('/ajax/sync/draft_pictures/<int:template_id>',
                  methods=['PUT'])
@login_required
def ajax_sync_draft_pictures(template_id):
    """
    create by ld_wu 2018-01-03
    同步图片到产品模板
    :return:
    """
    data = request.get_json()
    pictures = data.get('pictures', list())
    template = ProductTemplate.get_by_id(template_id)
    if template:
        template.pictures = pictures
    return jsonify(
        dict(
            status=0
        )
    )


@product_bp.route('/ajax/sync/draft_param', methods=['PUT'])
@login_required
def ajax_sync_draft_param():
    """
    create by ld_wu 2018-01-03
    同步技术参数值到产品技术参数
    :return:
    """
    data = request.get_json()
    value = data['value']
    param_id = data['param_id']
    param = ProductToParam.get_by_id(param_id)
    if param:
        param.value = value
    return jsonify(
        dict(
            status=0
        )
    )


@product_bp.route('/ajax/sync/draft_spec/<int:template_id>', methods=['PUT'])
@login_required
def ajax_sync_draft_spec(template_id):
    """
    create by ld_wu 2018-01-04
    同步产品规格
    :return:
    """
    data = request.get_json()
    spec_settings = data['spec_settings']
    template = ProductTemplate.get_by_id(template_id)
    if template:
        template.spec_settings = spec_settings

    return jsonify(
        dict(
            status=0
        )
    )


@product_bp.route('/ajax/sync/draft_brand/<int:template_id>', methods=['PUT'])
@login_required
def ajax_sync_draft_brand(template_id):
    """
    create by ld_wu 2018-01-04
    同步品牌信息
    :return:
    """
    data = request.get_json()
    brand_id = data['brand_id']
    brand = Brand.get_by_id(brand_id)
    if brand:
        template = ProductTemplate.get_by_id(template_id)
        if template:
            template.brand_id = brand.id
            template.brand = brand.brand_name

    return jsonify(
        dict(
            status=0
        )
    )


@product_bp.route('/product_draft/<int:draft_id>/close', methods=['POST'])
@login_required
def close_product_draft(draft_id):
    """
    create by ld_wu 2018-01-03
    结束审核（修改）
    :return:
    """
    form = DraftApprovalForm()

    if form.validate_on_submit():
        draft = ProductDraft.query.get_or_404(draft_id)
        if draft.status != DraftStatus.HANDING.value:
            flash('该产品已经审核过了')
            return redirect(
                url_for('product_bp.product_draft_info', draft_id=draft_id)
            )
        draft.remark = form.remark.data
        draft.approval_at = get_timestamp()
        draft.status = form.status.data
        draft.approver_id = current_user.id
        flash('审核完成')
        return redirect(url_for('product_bp.product_draft_list'))
    if form.errors:
        flash('表单数据有误，请检查')
    return redirect(
        url_for('product_bp.product_draft_info', draft_id=draft_id)
    )


@product_bp.route('/ajax/upload/picture', methods=['POST'])
def ajax_upload_picture():
    """
    create by ld_wu 2018-01-04
    上传产品图片
    """
    upload_file = request.files['image_file']
    if not upload_file:
        return jsonify(dict(status=1))
    sub_dir = 'product_images'
    save_file_dir = os.path.join(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
        'api/img/{}'.format(sub_dir)
    )

    if not os.path.exists(save_file_dir):
        os.makedirs(save_file_dir)
    file_name = '{}_{}'.format(get_timestamp(), upload_file.filename)
    save_file_path = os.path.join(
        save_file_dir,
        file_name
    )
    upload_file.save(save_file_path)
    host = os.environ.get('img_host')
    url = urllib.parse.urljoin(host, '{}/{}'.format(sub_dir, file_name))
    return jsonify(
        dict(
            status=0,
            url=url
        )
    )

    # upload_file = request.files['image_file']
    # if not upload_file:
    #     return jsonify(dict(status=1))
    # sub_dir = 'product_images'
    # file_name = '{}_{}'.format(get_timestamp(), upload_file.filename)
    #
    # today = date.today()
    # upload_key = '{}/{}{}/{}'.format(
    #     sub_dir, today.year, today.month, file_name
    # )
    #
    # bucket_name = ''
    # url = QiNiu().upload(bucket_name, upload_key, upload_file.read())
    #
    # return jsonify(
    #     dict(
    #         status=0,
    #         url=url
    #     )
    # )
